package yang.yu.sessioncache.redis;

import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.dayatang.configuration.Configuration;
import org.dayatang.configuration.ConfigurationFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.io.Closeable;
import java.io.IOException;
import java.io.Serializable;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

/**
 * Created by yyang on 15/9/8.
 */
public class RedisClient implements Closeable {

    private JedisPool jedisPool;

    private JedisTemplate template;

    private Configuration configuration = new ConfigurationFactory().fromClasspath("/redis.properties");

    public RedisClient() {
        String host = configuration.getString("host", "127.0.0.1");
        int port = configuration.getInt("port", 6379);
        String password = configuration.getString("password", "");
        if (StringUtils.isBlank(password)) {
            jedisPool = new JedisPool(createJedisPoolConfig(), host, port);
        } else {
            jedisPool = new JedisPool(createJedisPoolConfig(), host, port, 1000, password);
        }
        template = new JedisTemplate(jedisPool);
    }

    public RedisClient(String host, int port) {
        jedisPool = new JedisPool(createJedisPoolConfig(), host, port);
        template = new JedisTemplate(jedisPool);
    }

    public RedisClient(String host, int port, String password) {
        jedisPool = new JedisPool(createJedisPoolConfig(), host, port, 10000, password);
        template = new JedisTemplate(jedisPool);
    }

    public Optional<Object> get(final String key) {
        return template.execute(jedis -> {
            final byte[] keyBytes = SerializationUtils.serialize(key);
            if (jedis.exists(keyBytes)) {
                Object result = SerializationUtils.deserialize(jedis.get(keyBytes));
                return Optional.of(result);
            } else {
                return Optional.empty();
            }
        });
    }

    public Map<String, Object> get(final String... keys) {
        return template.execute(jedis -> {
            final Map<String, Object> values = new HashMap<String, Object>();
            for (String key : keys) {
                byte[] keyBytes = SerializationUtils.serialize(key);
                values.put(key, SerializationUtils.deserialize(jedis.get(keyBytes)));
            }
            return values;
        });
    }

    public void put(final String key, final Object value) {
        template.execute(jedis -> {
            byte[] keyBytes = SerializationUtils.serialize(key);
            byte[] valueBytes = SerializationUtils.serialize((Serializable) value);
            jedis.set(keyBytes, valueBytes);
            return null;
        });
    }

    public void put(String key, Object value, Date expiry) {
        long livingSeconds = (expiry.getTime() - new Date().getTime()) / 1000;
        put(key, value, livingSeconds);
    }

    public void put(final String key, final Object value, final long livingSeconds) {
        template.execute(jedis -> {
            byte[] keyBytes = SerializationUtils.serialize(key);
            byte[] valueBytes = SerializationUtils.serialize((Serializable) value);
            jedis.setex(keyBytes, (int) livingSeconds, valueBytes);
            return null;
        });
    }

    public boolean remove(final String key) {
        return template.execute(jedis -> {
            final byte[] keyBytes = SerializationUtils.serialize(key);
            if (!jedis.exists(keyBytes)) {
                return false;
            }
            try {
                jedis.del(SerializationUtils.serialize(key));
                return true;
            } catch (Exception e) {
                return false;
            }
        });
    }

    public boolean containsKey(final String key) {
        return template.execute(jedis -> jedis.exists(SerializationUtils.serialize(key)));
    }

    private JedisPoolConfig createJedisPoolConfig() {
        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(configuration.getInt("pool.max.total", -1));
        config.setMaxIdle(configuration.getInt("pool.max.idle", -1));
        config.setMaxWaitMillis(configuration.getLong("pool.max.wait.millis", 1000l));
        config.setTestOnBorrow(configuration.getBoolean("pool.test.on.borrow", true));
        config.setTestOnReturn(configuration.getBoolean("pool.test.on.return", false));
        return config;
    }

    @Override
    public void close() throws IOException {
        jedisPool.close();
    }
}
